package com.xx.happychatboot.middleware.netty.serve;

import com.xx.happychatboot.middleware.netty.handller.MyHeaderCollectHandler;
import com.xx.happychatboot.middleware.netty.handller.NettyWebSocketServerHandler;
import com.xx.happychatboot.middleware.netty.strategy.websocket.AbstractWebsocketStrategy;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.NettyRuntime;
import io.netty.util.concurrent.Future;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * netty服务配置类
 */
@Slf4j
@Configuration
public class NettyWebSocketServer {
    // netty端口
    public static final int WEB_SOCKET_PORT = 8090;

    // netty拦截器
    public static final NettyWebSocketServerHandler NETTY_WEB_SOCKET_SERVER_HANDLER = new NettyWebSocketServerHandler();

    // 处理连接用的线程池执行器 --> 用于接收客户端连接
    private EventLoopGroup bossGroup = new NioEventLoopGroup(1);

    // 处理I/O的线程池执行器 --> 用于处理已连接客户端
    private EventLoopGroup workerGroup = new NioEventLoopGroup(NettyRuntime.availableProcessors());

    /**
     * netty 初始化启动发放  --- 执行 run 方法
     *
     * @throws InterruptedException
     */
    /*@PostConstruct //该注解用于初始化执行
    public void start() throws InterruptedException {
        run();
    }*/

    /**
     * netty 销毁方法
     */
    @PreDestroy //该注解用于销毁,在对象销毁前会立即执行该方法
    public void destroy() {
        // 安全地关闭事件循环组的相关资源 ---> 释放所有线程
        Future<?> bossGroupShutDown = bossGroup.shutdownGracefully();
        Future<?> workerGroupShutDown = workerGroup.shutdownGracefully();
        // 阻塞当前线程，等待bossGroup和workerGroup的事件循环线程组正常关闭，确保关闭操作已经完成
        bossGroupShutDown.syncUninterruptibly();
        workerGroupShutDown.syncUninterruptibly();
        log.info("关闭 ws server 成功");
    }

    /**
     * 启动方法
     *
     * @throws InterruptedException
     */
    public void run() throws InterruptedException {
        // 服务器启动引导对象
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup)
                // 指定使用的通道
                .channel(NioServerSocketChannel.class)
                // 服务器套接字的积压队列大小，指定在服务器处理请求之前可以排队的连接数。
                .option(ChannelOption.SO_BACKLOG, 128)
                //保持活动连接的TCP选项，如果启用，则服务器将定期向客户端发送数据包以检查连接是否仍然有效。
                .option(ChannelOption.SO_KEEPALIVE, true)
                // 为 bossGroup 添加 日志处理器
                .handler(new LoggingHandler(LogLevel.INFO))
                // 自定义拦截器处理
                .childHandler(new ChannelInitializer<SocketChannel>() {

                    /**
                     * 自定义初始化执行方法 ---> 用于处理业务逻辑
                     * @param ch
                     * @throws Exception
                     */
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        // ChannelPipeline是用来管理和处理ChannelHandler的容器，它负责处理入站和出站的消息流。
                        // ChannelPipeline是由一系列的ChannelHandler组成，这些Handler按顺序组成了一个处理链。
                        // 当消息从一个端点（入站或出站）流经一个ChannelPipeline时，每个Handler都有机会处理消息或者将消息传递给下一个Handler。
                        ChannelPipeline pipeline = ch.pipeline();

                        // 因为使用http协议，所以需要使用http的编码器，解码器
                        pipeline.addLast(new HttpServerCodec());

                        // ChunkedWriteHandler是Netty提供的一个用于处理写入大数据流的ChannelHandler。
                        // 它的主要作用是将数据按照一定的大小（chunk）进行分块传输，以避免内存消耗过大和提高传输效率。
                        pipeline.addLast(new ChunkedWriteHandler());

                        /**
                         * 说明：
                         *  1. http数据在传输过程中是分段的，HttpObjectAggregator可以把多个段聚合起来；
                         *  2. 这就是为什么当浏览器发送大量数据时，就会发出多次 http请求的原因
                         */
                        pipeline.addLast(new HttpObjectAggregator(8192));

                        // 保存请求头
                        pipeline.addLast(new MyHeaderCollectHandler());

                        /**
                         * 说明：
                         *  1. 对于 WebSocket，它的数据是以帧frame 的形式传递的；
                         *  2. 可以看到 WebSocketFrame 下面有6个子类
                         *  3. 浏览器发送请求时： ws://localhost:7000/hello 表示请求的uri
                         *  4. WebSocketServerProtocolHandler 核心功能是把 http协议升级为 ws 协议，保持长连接；
                         *      是通过一个状态码 101 来切换的
                         */
                        pipeline.addLast(new WebSocketServerProtocolHandler("/"));

                        // 自定义handler ，处理业务逻辑 ---> 放最后处理
                        pipeline.addLast(NETTY_WEB_SOCKET_SERVER_HANDLER);
                    }
                });
        // 启动服务器，监听端口，阻塞直到启动成功
        ChannelFuture channelFuture = serverBootstrap.bind(WEB_SOCKET_PORT).sync();
        if (channelFuture.isSuccess()) {
            log.info("Netty 启动成功 端口为: {}", WEB_SOCKET_PORT);
        }
    }
}
